package com.xzzz.irda.guardcat.core.metric.dubbo;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ReflectUtil;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.common.status.Status;
import org.apache.dubbo.common.store.DataStore;
import org.apache.dubbo.common.threadpool.manager.DefaultExecutorRepository;
import org.apache.dubbo.common.threadpool.manager.ExecutorRepository;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author xzzz
 * @since 1.0.0
 */
public class DubboThreadPoolInfo {

    /**
     * 获取线程池名称
     */
    private String poolName;

    /**
     * 线程池中核心线程数量
     */
    private Integer corePoolSize;

    /**
     * 线程池中曾经创建过的最大线程数量
     */
    private Integer largestPoolSize;

    /**
     * 线程池中允许的最大线程数量
     */
    private Integer maximumPoolSize;

    /**
     * 线程池中当前线程数
     */
    private Integer currentPoolSize;

    /**
     * 线程池中正在主动执行任务的线程的大致数量
     */
    private Integer activeCount;

    /**
     * 线程池中已执行完成的大致任务总数
     */
    private Long completedTaskCount;

    /**
     * 线程池中需要执行的任务数量
     */
    private Long taskCount;

    /**
     * 线程池中队列中的任务数量【队列中还没执行的任务量】
     */
    private Integer queueTaskCount;

    /**
     * 线程池中队列的剩余容量【可存放接下来任务的容量】
     */
    private Integer queueRemainingCapacity;


    /**
     * apache dubbo 线程池监控
     */
    @SuppressWarnings("all")
    public static List<DubboThreadPoolInfo> getApacheDubboThreadPoolInfos() {
        ExecutorRepository executorRepository = ExtensionLoader.getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
        if (null == executorRepository) {
            return null;
        }
        if (executorRepository instanceof DefaultExecutorRepository) {
            DefaultExecutorRepository defaultExecutorRepository = (DefaultExecutorRepository) executorRepository;
            Field dataField = null;
            dataField = ReflectUtil.getField(DefaultExecutorRepository.class, "data");
            dataField.setAccessible(true);

            ConcurrentMap<String, ConcurrentMap<Integer, ExecutorService>> data = null;
            try {
                data = (ConcurrentMap<String, ConcurrentMap<Integer, ExecutorService>>) dataField.get(defaultExecutorRepository);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("获取 org.apache.dubbo.common.threadpool.manager.DefaultExecutorRepository 中属性 data 值失败：" + e.getMessage());
            }
            if (MapUtil.isEmpty(data)) {
                return null;
            }
            ConcurrentMap<Integer, ExecutorService> executors = data.get(CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY);
            if (MapUtil.isEmpty(executors)) {
                return null;
            }

            List<DubboThreadPoolInfo> dubboThreadPoolInfoList = new ArrayList<>();
            DubboThreadPoolInfo dubboThreadPoolInfo;
            ExecutorService executor;
            ThreadPoolExecutor threadPool;
            for (Map.Entry<Integer, ExecutorService> entry : executors.entrySet()) {
                dubboThreadPoolInfo = new DubboThreadPoolInfo();
                // dubbo 的端口
                dubboThreadPoolInfo.setPoolName(entry.getKey() + " 端口的 dubbo 线程池");
                // Dubbo线程池，一个端口对应一个线程池
                executor = (ExecutorService) entry.getValue();
                if (executor instanceof ThreadPoolExecutor) {
                    threadPool = (ThreadPoolExecutor) executor;

                    dubboThreadPoolInfo.setCorePoolSize(threadPool.getCorePoolSize());
                    dubboThreadPoolInfo.setLargestPoolSize(threadPool.getLargestPoolSize());
                    dubboThreadPoolInfo.setMaximumPoolSize(threadPool.getMaximumPoolSize());
                    dubboThreadPoolInfo.setCurrentPoolSize(threadPool.getPoolSize());
                    dubboThreadPoolInfo.setActiveCount(threadPool.getActiveCount());
                    dubboThreadPoolInfo.setCompletedTaskCount(threadPool.getCompletedTaskCount());
                    dubboThreadPoolInfo.setTaskCount(threadPool.getTaskCount());
                    dubboThreadPoolInfo.setQueueTaskCount(threadPool.getQueue().size());
                    dubboThreadPoolInfo.setQueueRemainingCapacity(threadPool.getQueue().remainingCapacity());
                }
                dubboThreadPoolInfoList.add(dubboThreadPoolInfo);
            }
            return dubboThreadPoolInfoList;
        }
        return null;
    }

    /**
     * alibaba dubbo 线程池监控
     */
    @Deprecated
    public static void getAlibabaDubboThreadPoolInfos() {
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        Map<String, Object> threadPoolMap = dataStore.get(CommonConstants.EXECUTOR_SERVICE_COMPONENT_KEY);
        StringBuilder builder = new StringBuilder();
        Status.Level level = Status.Level.OK;
        for (Map.Entry<String, Object> entry : threadPoolMap.entrySet()) {
            String port = entry.getKey();
            ExecutorService executor = (ExecutorService) entry.getValue();
            if (executor instanceof ThreadPoolExecutor) {
                ThreadPoolExecutor threadPool = (ThreadPoolExecutor) executor;
                boolean ok = threadPool.getActiveCount() < threadPool.getMaximumPoolSize() - 1;
                Status.Level lvl = Status.Level.OK;
                if (!ok) {
                    level = Status.Level.WARN;
                    lvl = Status.Level.WARN;
                }
                if (builder.length() > 0) {
                    builder.append(";");
                }
                builder.append("Pool Status：").append(lvl)
                        .append(", max：").append(threadPool.getMaximumPoolSize())
                        .append(", core：").append(threadPool.getCorePoolSize())
                        .append(", largest：").append(threadPool.getLargestPoolSize())
                        .append(", active：").append(threadPool.getActiveCount())
                        .append(", task：").append(threadPool.getTaskCount())
                        .append(", completedTask：").append(threadPool.getCompletedTaskCount())
                        .append(", service port：").append(port);

                System.out.println(builder);
            }
        }
    }

    public String getPoolName() {
        return poolName;
    }

    public void setPoolName(String poolName) {
        this.poolName = poolName;
    }

    public Integer getCorePoolSize() {
        return corePoolSize;
    }

    public void setCorePoolSize(Integer corePoolSize) {
        this.corePoolSize = corePoolSize;
    }

    public Integer getLargestPoolSize() {
        return largestPoolSize;
    }

    public void setLargestPoolSize(Integer largestPoolSize) {
        this.largestPoolSize = largestPoolSize;
    }

    public Integer getMaximumPoolSize() {
        return maximumPoolSize;
    }

    public void setMaximumPoolSize(Integer maximumPoolSize) {
        this.maximumPoolSize = maximumPoolSize;
    }

    public Integer getCurrentPoolSize() {
        return currentPoolSize;
    }

    public void setCurrentPoolSize(Integer currentPoolSize) {
        this.currentPoolSize = currentPoolSize;
    }

    public Integer getActiveCount() {
        return activeCount;
    }

    public void setActiveCount(Integer activeCount) {
        this.activeCount = activeCount;
    }

    public Long getCompletedTaskCount() {
        return completedTaskCount;
    }

    public void setCompletedTaskCount(Long completedTaskCount) {
        this.completedTaskCount = completedTaskCount;
    }

    public Long getTaskCount() {
        return taskCount;
    }

    public void setTaskCount(Long taskCount) {
        this.taskCount = taskCount;
    }

    public Integer getQueueTaskCount() {
        return queueTaskCount;
    }

    public void setQueueTaskCount(Integer queueTaskCount) {
        this.queueTaskCount = queueTaskCount;
    }

    public Integer getQueueRemainingCapacity() {
        return queueRemainingCapacity;
    }

    public void setQueueRemainingCapacity(Integer queueRemainingCapacity) {
        this.queueRemainingCapacity = queueRemainingCapacity;
    }

    @Override
    public String toString() {
        return "DubboThreadPoolInfo{" +
                "poolName='" + poolName + '\'' +
                ", corePoolSize=" + corePoolSize +
                ", largestPoolSize=" + largestPoolSize +
                ", maximumPoolSize=" + maximumPoolSize +
                ", currentPoolSize=" + currentPoolSize +
                ", activeCount=" + activeCount +
                ", completedTaskCount=" + completedTaskCount +
                ", taskCount=" + taskCount +
                ", queueTaskCount=" + queueTaskCount +
                ", queueRemainingCapacity=" + queueRemainingCapacity +
                '}';
    }
}
